Así que tengo esta API que devuelve un diccionario de [String: Any] , sé que lo que viene como Any es Decodable o una matriz de Decodable , sin embargo, no puedo descifrar cómo tomar ese diccionario y decodificarlo. a alguna estructura:
Lo que tengo es básicamente así:
public func call<T: Codable> (completion handler: @escaping (T?) -> ()) { let promise = api.getPromise () promise.done (on: DispatchQueue.main, { (results: [String:Any]) let decodedResults:T? = results.decode (as: T.self) // <-- this is what I want handler (decodedResults) }) }Intenté convertirlo en datos y luego decodificarlo con:
let values = results.compactMap { $0.value } let data = JSONSerialization.data (withJSONObject: values, options: []) let decodedResult = JSONDecoder().decode(T.self, from: data) pero siempre falla con NSInvalidArgumentException , ¿alguna idea de cómo solucionar esto?
Otra cosa que estaba tratando de lograr pero no lo logré es convertir los valores en una tupla, pero por lo que pude encontrar, no es posible crear tuplas dinámicamente.
Los decodificadores convierten los datos en valores decodificables. No tienen nada que ver con los tipos [String: Any] ni con ningún otro tipo que no sea de datos. Entonces, si desea ejecutarlo a través de un decodificador, debe convertirlo a JSON codificado en datos.
Si los resultados de [String: Any] son exclusivamente tipos seguros para JSONSerialization (matrices, diccionarios, cadenas, números, nulos), entonces JSONSerialization.data(withJSONObject:options:) le permitiría volver a los datos, para que pueda volver a decodificarlos. eso. Su código no solo vuelve a codificar sus resultados, primero lo convierte en una matriz:
let values = results.compactMap { $0.value } let data = JSONSerialization.data (withJSONObject: values, options: []) Eso es muy extraño. ¿De verdad quieres crear una matriz aquí y tirar las llaves? Entonces esperaría que su JSONDecoder().decode() decodifique [T].self en lugar de T.self . Entonces, esperaría el siguiente código (siempre que su [String: Any] sea seguro para JSON):
public func call<T: Decodable>(completion handler: @escaping (T?) -> ()) { let promise = api.getPromise() promise.done(on: .main) { (results: [String:Any]) in guard JSONSerialization.isValidJSONObject(results) else { handler(nil) return } let data = JSONSerialization.data(withJSONObject: results) let decodedResults = try? JSONDecoder().decode(T.self, from: data) handler(decodedResults) } } En los comentarios, observa que los datos decodificados (el [String: Any] ) no están hechos de primitivos. En ese caso, no es posible volver a codificarlo con JSONSerialization. Tendrás que pasar el [String: Any] a algo que sepa cómo manejarlo. Por ejemplo:
protocol DictionaryDecodable { init?(dictionary: [String: Any]) } public func call<T: DictionaryDecodable>(completion handler: @escaping (T?) -> ()) { let promise = api.getPromise () promise.done(on: .main) { (results: [String:Any]) handler(T.init(dictionary: results)) } } Tus tipos necesitarán implementar un init?(dictionary:) que pueda decodificar sus propios valores a partir de una [String: Any] .